package app

import (
	"cserver/api/exmq"
	"cserver/api/exredis"
	"cserver/api/impl"
	"fmt"
	"github.com/go-redis/redis"
	"github.com/gorilla/websocket"
	"github.com/streadway/amqp"
	"log"
	"net/http"
	"runtime"
	"time"
)

var (
	addr      string
	wsPath    string
	err       error
	redisConn *redis.Client
	mqConn    *amqp.Connection
	mqChan    *amqp.Channel
)

//开启http服务
func HttpStart(addr string, wsPath string) {
	//链接redis
	redisConn = exredis.Conn()
	//链接rabbitmq
	if mqConn, err = exmq.MqConn(); err != nil {
		log.Panic(err)
		return
	}
	//开启http服务
	http.HandleFunc(wsPath, Ws)
	http.ListenAndServe(addr, nil)
}

//ws协议升级操作
func Ws(w http.ResponseWriter, r *http.Request) {
	var (
		wsConn          *websocket.Conn
		mqChan          *amqp.Channel
		conn            *impl.Connection
		err             error
		data            []byte
		ClientStatusObj ClientStatus
		SendID          string
	)

	var upgrader = websocket.Upgrader{
		// socket配置跨域
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	fmt.Println("")
	//建立socket链接
	if wsConn, err = upgrader.Upgrade(w, r, nil); err != nil {
		log.Println(err)
		return
	}
	//读取第一条消息确认身份[阻塞运行读取第一条消息]
	if _, data, err = wsConn.ReadMessage(); err != nil {
		log.Println(err)
		goto ERR
	}
	//身份认证
	if SendID, ClientStatusObj, err = Created(data, redisConn); err != nil {
		log.Println(err)
		goto ERR
	}
	//建立rabbitmq通道
	if mqChan, err = exmq.MqChan(mqConn); err != nil {
		log.Println(err)
		goto ERR
	}
	//————————————————————开始收发服务 [协程安全]————————————————————
	//通道开启
	if conn, err = impl.InitConnection(wsConn); err != nil {
		log.Println(err)
		goto ERR
	}
	//开启读协程 [goroutine]
	go conn.ReadLoop()
	//开启写协程 [goroutine]
	go conn.WriteLoop()

	//监听mq消息推送到客户端 [goroutine]
	go WriteMqMessage(ClientStatusObj, conn, mqChan)
	//心跳保持 [goroutine]
	go Heart(conn, sendID)
	//读取消息发送到mq中去 [阻塞运行]
	ReadMessageInMq(ClientStatusObj, conn, mqChan)
	//————————————————————结束收发消息————————————————————
ERR:
	//删除redis缓存 下线处理
	unlike(SendID)
	conn.Close()
	wsConn.Close()
	mqChan.Close()
	runtime.GC()

}

// 心跳保持
func Heart(conn *impl.Connection, SendID string) {
	for {
		if err = conn.WriteMessage([]byte(SendID + "heart")); err != nil {
			conn.Close()
			break
		}
		time.Sleep(time.Second * 1)
	}
}

// 下线操作
func unlike(SendID string) {
	if err = exredis.Del(redisConn, SendID); err != nil {
		log.Println(err)
	}
}

//监听mq消息放入到写协程中去
func WriteMqMessage(ClientStatusObj ClientStatus, conn *impl.Connection, mqChan *amqp.Channel) {
	var (
		rMqExchange   string
		rMqQueue      string
		rMqRoutingKey string
		consumerName  string
		megs          <-chan amqp.Delivery
		err           error
	)
	rMqExchange = ClientStatusObj.RMqExchange
	rMqQueue = ClientStatusObj.RMqQueue
	rMqRoutingKey = ClientStatusObj.RMqRoutingKey
	consumerName = ClientStatusObj.ConsumerName
	if megs, err = exmq.Receive(mqChan, rMqExchange, rMqQueue, rMqRoutingKey, consumerName); err != nil {
		conn.Close()
	}
	go func() {
		for d := range megs {
			res := process(d.Body, conn)
			if res {

				d.Ack(true)
			}
		}
	}()
}

//监听mq消息并写入消息
func process(data []byte, conn *impl.Connection) bool {
	if err := conn.WriteMessage(data); err != nil {
		log.Println(err)
		return false
	}
	return true
}

//读取消息放入rabbitmq队列中去
func ReadMessageInMq(ClientStatusObj ClientStatus, conn *impl.Connection, mqChan *amqp.Channel) {
	var (
		err           error
		sMqExchange   string
		sMqQueue      string
		rMqRoutingKey string
		messageBody   []byte
	)

	sMqExchange = ClientStatusObj.SMqExchange
	sMqQueue = ClientStatusObj.SMqQueue
	rMqRoutingKey = ClientStatusObj.SMqRoutingKey

	for {
		if messageBody, err = conn.ReadMessage(); err != nil {
			log.Println(err)
			conn.Close()
			break
		}
		if err = exmq.Send(mqChan, sMqExchange, sMqQueue, rMqRoutingKey, messageBody); err != nil {
			log.Println(err)
		}
	}
}
